-- CombatStatusM
-- Create by panyl
-- 战斗状态模块

module("CombatStatusM", package.seeall);

-- 所有的状态信息
local statusInfo = {};

-- 状态名称与ID的映射关系
local aliasIdMap = {};

-- 子规则
local rules = {};

-- 内部函数声明

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    -- 将所有的状态信息转换下
    local combat_status_CSV = get_combat_status_CSV();
    for i = 1, #combat_status_CSV do
        local row = combat_status_CSV[i];
        statusInfo[row.status] = row;
        aliasIdMap[row.alias] = row.status;

        -- 必须有子模块
        assert(rules[row.rule]);
    end

    -- TODO: 这里赋值nil不起效，暂不知为何
    combat_status_CSV = {};
end

-- 模块初始化
function init()
    if not _init then
        -- 载入所有的规则处理子模块
        rules = LOAD_PATH("game/logic/module/combat_status");
        loadCsv();
    end
end

-- 检索
function query(status, path)
    local m = statusInfo[status];

    if not m or not path then
        return m;
    end

    return m[path];
end

-- 是否有效的状态
function isValidDebuff(status)
    if type(status) == "string" then
        return type(aliasIdMap[status]) == "number";
    end

    return type(statusInfo[status]) == "table";
end

-- 根据状态ID取得别名
function getStatusAlias(status)
    local id = getStatusId(status);

    return statusInfo[id].alias;
end

-- 根据别名取得id
function getStatusId(status)
    if type(status) == "number" then
        return status;
    end

    return aliasIdMap[status];
end

-- 取得状态信息
function getStatusInfo(status)
    return statusInfo[getStatusId(status)];
end

-- 判断玩家是否有某个状态
function checkStatus(target, status)
    local id = getStatusId(status);
    local combatStatus = target.dbase:query("combat_status", {});

    return type(combatStatus[id]) == "table";
end

-- 根据规则获取状态
function getStatusByRule(rule)
    local list = {};
    for status, info in pairs(statusInfo) do
        if rule == info.rule then
            table.insert(list, status);
        end
    end

    table.sort(list);

    return list;
end

-- 获取排斥状态
function getExclusionStatus(status)
    local cfg = getStatusInfo(status);
    local exclusionType = cfg["exclusion_type"] or {};
    local exclusionStatus = cfg["exclusion_status"] or {};
    local list = {};        -- 用新数组保存

    for _, statusType in pairs(exclusionType) do
        list = table.append(list, getStatusByType(statusType));
    end

    return table.append(list, exclusionStatus);
end

-- 根据类型获取状态
function getStatusByType(statusType)
    local ret = {};

    for _, row in pairs(statusInfo) do
        if statusType == row.type then
            table.insert(ret, row.status);
        end
    end

    return ret;
end

-- 赋予状态
function applyStatus(target, status, condition)
    Profiler.funcBegin("CombatStatusM.applyStatus");

    -- 必须的有开始回合和结束回合
    assert(type(condition["end_round"]) == "number");

    local id = getStatusId(status);
    assert(id, "没有此状态");

    -- 免疫状态
    if immunityStatus(target, status) then
        return false;
    end

    -- 持续时间减少
    local endRound = reduceDuringTime(target, status, condition);
    condition["end_round"] = endRound;

    -- 清除本状态排斥的状态
    -- 为了防止清除状态时回调脚本同时清除了其它状态，所以不能预先
    -- 取出"status"字段而应该在每次判定时都重新读取
    local allExclusion = getExclusionStatus(id);
    for _, exclusionStatus in pairs(allExclusion) do
        clearStatus(target, exclusionStatus, nil, false);
    end

    -- 排斥自己
    clearStatus(target, status, nil, false);

    -- 某些状态不被自己顶掉
    if not FormulaM.invoke("NOT_REJECT_SELF", id) then
        clearStatus(target, status);
    end

    -- 记录附加的状态
    local combatStatus = target.dbase:query("combat_status", {});
    combatStatus[id] = table.deepcopy(condition);
    target.dbase:set("combat_status", combatStatus);

    -- 刷新下状态属性
    PropM.refreshEffect(target, "combat_status");

    -- 可能需要抛事件
    local mod = rules[query(id, "rule")];
    if mod and mod.fireEvent then
        mod.fireEvent(target, status, condition, "apply");
    end

    -- 如果是迷宫内的战斗，抛出事件
    if DungeonM.getState() == DUNGEON_GAMING and OBJECT_TYPE_SKYFIGHTER ~= target.type then
        EventMgr.fire(event.APPLY_COMBAT_STATUS, {["target"] = target, ["pos"] = target:getPos(), ["status"] = status, ["round"] = condition["end_round"]});
    end

    Profiler.funcEnd("CombatStatusM.applyStatus");
    return true;
end

-- 减少持续时间
function reduceDuringTime(target, status, condition)
    local endRound = condition["end_round"];
    local round = getRound(target);
    -- 取不到回合，或持续本层的状态，不处理
    if not round or endRound == -1 then
        return endRound;
    end

    local duringTime = endRound - round;
    local id = getStatusId(status);

    -- 减少某类状态的持续时间
    local statusType = statusInfo[id]["type"];
    local prop = PropM.combine(target, "reduce_during_time", statusType);
    duringTime = PropM.apply(prop, duringTime);
    endRound = round + duringTime;

    return endRound;
end

-- 删除某个状态
function clearStatus(target, status, breakOff, refresh)
    if nil == breakOff then
        -- 默认是true
        breakOff = true;
    end

    if nil == refresh then
        -- 默认是true
        refresh = true;
    end

    local id = getStatusId(status);

    local combatStatus = target.dbase:query("combat_status", {});

    if not combatStatus[id] then
        -- 没有此状态
        return;
    end

    -- 清除属性处理
    local mod = rules[query(id, "rule")];
    local condition = combatStatus[id];

    -- 删除
    combatStatus[id] = nil;

    -- 被强行中断的，需要走个清楚属性的流程(如果是自动到期的会走此流程)
    if breakOff then
        local props = mod.fetchProps(target, status, condition);
        for _, prop in ipairs(props) do
            PropM.clearProp(target, prop);
        end
    end

    -- 子模块处理一下
    if mod.clear then
        mod.clear(target, id, condition);
    end

    if refresh then
        PropM.refreshEffect(target, "combat_status");
    end

    -- 可能需要抛事件
    if mod and mod.fireEvent then
        mod.fireEvent(target, status, condition, "clear");
    end
end

-- 根据类型清除状态
function clearStatusByType(target, statusType)
    local combatStatus = target.dbase:query("combat_status", {});
    local list = table.keys(combatStatus);

    local id;
    for _, status in pairs(list) do
        -- 清除掉该类状态
        -- SLIMEC-6177 临时先这样处理下
        id = getStatusId(status);
        if statusType == statusInfo[status]["type"] and 
            query(id, "can_clear_by_type") ~= 1 then
            
            clearStatus(target, status);
        end
    end

    PropM.refreshEffect(target, "combat_status");
end

-- 根据规则清除状态
function clearStatusByRule(target, rule)
    local combatStatus = target.dbase:query("combat_status", {});
    local list = table.keys(combatStatus);

    for _, status in pairs(list) do
        -- 清除掉该类状态
        if rule == statusInfo[status]["rule"] then
            clearStatus(target, status);
        end
    end

    PropM.refreshEffect(target, "combat_status");
end

-- 清除过期的状态
function clearOverdueStatus(target, round)
    local combatStatus = target.dbase:query("combat_status", {});
    local list = table.keys(combatStatus);
    local modify = false;

    for _, status in pairs(list) do
        local condition = combatStatus[status];

        if condition["end_round"] ~= -1 and
            condition["end_round"] <= round then
            -- 过期状态删除
            clearStatus(target, status, false);
            -- combatStatus[status] = nil;

            modify = true;
        end
    end

    -- 刷新下属性
    if modify then
        PropM.refreshEffect(target, "combat_status");
    end
end

-- 还原属性
function resetAllStatus(target, combatStatus)
    local list = table.keys(combatStatus);
    local oldStatus = target.dbase:query("combat_status", {});

    for _, status in pairs(list) do
        -- 不是永久保存的不管
        if getStatusInfo(status)["save"] == 1 then
            if type(oldStatus[status]) ~= "table" then
                -- 使用掉了
                combatStatus[status] = nil;

                -- 走清除流程
                clearStatus(target, status);
            end
        end
    end

    for _, status in pairs(table.keys(oldStatus)) do
        -- 不是永久保存的不管
        if getStatusInfo(status)["save"] == 1 then
            if combatStatus[status] ~= "table" then
                -- 暂离时附上的状态
                combatStatus[status] = oldStatus[status];
            end
        end
    end

    -- 更新
    updateStatus(target, combatStatus);
end

-- 清除状态
function clearAllStatus(target, keepSave)
    local combatStatus = target.dbase:query("combat_status", {});
    local list = table.keys(combatStatus);
    local modify = false;

    for _, status in pairs(list) do
        local condition = combatStatus[status];

        if not keepSave or 1 ~= getStatusInfo(status)["save"] then
            -- combatStatus[status] = nil;

            -- 走清除流程
            clearStatus(target, status);

            modify = true;
        end
    end

    -- 刷新下属性
    if modify then
        PropM.refreshEffect(target, "combat_status");
    end
end

-- 更新状态
function updateStatus(target, combatStatus)
    local oldStatus = target.dbase:query("combat_status", {});

    target.dbase:set("combat_status", combatStatus);
    PropM.refreshEffect(target, "combat_status");

    -- 返回旧的状态
    return oldStatus;
end

-- 获取所有的状态属性
function fetchAllProps(target, statusType)
    local combatStatus = target.dbase:query("combat_status", {});
    local props = {};

    for status, condition in pairs(combatStatus) do
        local row = getStatusInfo(status);

        if not statusType or statusType == row.type then
            local mod = rules[row.rule];
            props = table.append(props, mod.fetchProps(target, status, condition));
        end
    end

    return props;
end

-- 获取状态描述
function getStatusDesc(target, status, condition)
    local row = getStatusInfo(status);

    condition = condition or queryCondition(target, status);

    -- TODO:
    local mod = rules[row.rule];
    return mod.desc(target, status, condition);
end

-- 获取某个状态的名字
function getStatusName(target, status)
    local condition = queryCondition(target, status);
    local cfg = getStatusInfo(status);

    -- 如果有配置了名字，则优先用配置的名字
    if cfg.name and #cfg.name > 0 then
        return cfg.name;
    end

    if not condition then
        -- 玩家没有此状态
        return "";
    end

    -- 如果没有属性
    local prop = condition.prop;
    if not prop then
        return "";
    end

    if type(prop[1]) == "table" then
        prop = prop[1];
    end

    -- 用属性名字
    local propId = prop[1];

    local propName = PropM.query(propId, "alias");

    return propName or "";
end

-- 取得状态的condition信息
function queryCondition(target, status)
    local id = getStatusId(status);

    local combatStatus = target.dbase:query("combat_status", {});
    return combatStatus[id];
end

-- 是否免疫状态
function immunityStatus(target, status)
    local info = getStatusInfo(status);

    -- 被辅助了（光环怪）,全场同伴免疫所有异常状态
    local prop = PropM.combine(target, "aid_immunity", 1);
    if prop[3] > 0 then
        if info.type == COM_STATUS_DEBUFF or info.type == COM_STATUS_DAMAGE
            or (info.type == COM_STATUS_SPECIAL and target.type ~= OBJECT_TYPE_USER) then
            return true;
        end
    end

    -- 免疫所有异常状态
    prop = PropM.combine(target, "immunity", "abnormal");
    if PropM.apply(prop, 1) > 0 then
        if info.type == COM_STATUS_DEBUFF or info.type == COM_STATUS_DAMAGE then
            return true;
        end
    end

    -- 低HP免疫异常状态
    if PropM.trigger(target, "hp_to_immunity", 1) then
        if info.type == COM_STATUS_DEBUFF or info.type == COM_STATUS_DAMAGE then
            return true;
        end
    end

    -- 特定冈布奥免疫特定状态
    local props = PropM.fetchProps(target, "slime_immunity", 1);
    for _, prop in ipairs(props) do
        if PropM.trigger(target, prop[1], prop[2], info.status) then
            return true;
        end
    end

    -- 免疫某种状态
    prop = PropM.combine(target, "immunity", info.alias);
    if PropM.apply(prop, 1) > 0 then
        return true;
    end

    -- 免疫某类状态
    prop = PropM.combine(target, "immunity", info.type)
    if PropM.apply(prop, 1) > 0 then
        return true;
    end

    -- 存在控制抗性时，免疫控制类技能
    if 1 == info.durance then
        prop = PropM.combine(target, "durance_resist", 1);
        if PropM.apply(prop, 1) > 0 then
            if target.isAudience and target:isAudience() then
                -- 观众就不要提示了
            elseif not target:isDead() then
                if prop[4] == -1 then
                    doAlert(getLocStr("no_durance2"));
                else
                    doAlert(string.format(getLocStr("no_durance"), prop[4] - getRound(target)));
                end
            end
            return true;
        end
    end

    prop = PropM.combine(target, "clean_debuf", 1);
    if prop[3] > 0 and (info.type == COM_STATUS_DEBUFF or info.type == COM_STATUS_DAMAGE) then
        return true;
    end

    return false;
end

-- 获取回合
function getRound(who)
    if who.type == OBJECT_TYPE_SKYFIGHTER then
        return SkyCombatM.getRound();
    else
        return CombatM.getRound();
    end
end

-- debuff类状态，需要特殊处理下，因为需要判断免疫的情况
function applyDebuff(target, status, condition, desc)

end

function reviveClean(who)
    -- 挂一个状态，回合结束再清除一次？
    local prop = { PropM.getPropId("clean_debuf"), 1, 1, CombatM.getRound() };
    PropM.record(who, prop, "mixed", true);

    clearDebuf(who);
end

-- 清除负面状态
function clearDebuf(who)
    -- 清除负面状态
    CombatStatusM.clearStatusByType(who, COM_STATUS_DEBUFF);
    CombatStatusM.clearStatusByType(who, COM_STATUS_DAMAGE);

    -- 抛事件
    EventMgr.fire(event.CLEAR_DEBUFF);
end
